package cz.martindobias.aladin;

import android.app.Activity;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.*;
import android.net.Uri;
import android.os.Handler;
import android.text.TextPaint;
import android.util.Log;

import java.io.IOException;
import java.util.*;
import java.util.regex.MatchResult;

public class AladinImageListViewAdapter implements ImageListViewAdapter {

    private static class ImageItem {
        public static final int TYPE_TEXT = 1;
        public static final int TYPE_BITMAP = 2;

        public int posX;
        public int posY;
        public int refWidth;
        public int refHeight;
        public int type;
        public String data;
    }

    protected final Activity activity;
    protected final String category;
    private final String overlayCategory;
    private final String baseCategory;
    protected final SortedMap<Integer, String> urls = new TreeMap<Integer, String>();
    private final Map<Integer, SortedMap<Integer, ImageItem>> bases = new HashMap<Integer, SortedMap<Integer, ImageItem>>();
    private final Map<Integer, SortedMap<Integer, ImageItem>> overlays = new HashMap<Integer, SortedMap<Integer, ImageItem>>();
    private OnChangeListener listener = null;
    private final Set<String> loadingImages = Collections.synchronizedSet(new HashSet<String>());

    public AladinImageListViewAdapter(String category, Cursor cursor, ImageBrowseActivity activity) {
        this(category, null, null, cursor, activity);
    }

    public AladinImageListViewAdapter(String category, String overlayCategory, Cursor cursor, ImageBrowseActivity activity) {
        this(category, null, overlayCategory, cursor, activity);
    }

    public AladinImageListViewAdapter(String category, String baseCategory, String overlayCategory, Cursor cursor, ImageBrowseActivity activity) {
        this.activity = activity;
        this.category = category;
        this.baseCategory = baseCategory;
        this.overlayCategory = overlayCategory;
        this.parseData(cursor);
    }

    private void parseData(Cursor cursor) {
        if(cursor != null) {
            int indexResultType = cursor.getColumnIndexOrThrow(ContentTables.COLUMN_RESULT_TYPE);
            int indexSequence = cursor.getColumnIndexOrThrow(ContentTables.COLUMN_SEQUENCE);
            int indexResult = cursor.getColumnIndexOrThrow(ContentTables.COLUMN_RESULT);

            while(!cursor.isAfterLast()) {
                String cat = cursor.getString(indexResultType);
                if(this.category != null && this.category.equals(cat)) {
                    this.urls.put(cursor.getInt(indexSequence), cursor.getString(indexResult));
                } else if((this.overlayCategory != null && this.overlayCategory.equals(cat))
                        || (this.baseCategory != null && this.baseCategory.equals(cat))) {
                    String result = cursor.getString(indexResult);
                    Log.d("AladinImageListViewAdapter", result);
                    Scanner scanner = new Scanner(result);
                    scanner.findInLine("^(-?\\d+)\\|(-?\\d+)\\|(-?\\d+)\\|(-?\\d+)\\|(-?\\d+)\\|((text)|(bitmap))\\|(.+)$");
                    MatchResult r = scanner.match();
                    ImageItem imageItem = new ImageItem();
                    imageItem.posX = Integer.parseInt(r.group(2));
                    imageItem.posY = Integer.parseInt(r.group(3));
                    imageItem.refWidth = Integer.parseInt(r.group(4));
                    imageItem.refHeight = Integer.parseInt(r.group(5));
                    imageItem.type = "bitmap".equals(r.group(6)) ? ImageItem.TYPE_BITMAP : ImageItem.TYPE_TEXT;
                    imageItem.data = r.group(9);

                    Map<Integer, SortedMap<Integer, ImageItem>> items;
                    if(cat.equals(this.overlayCategory)) {
                        items = this.overlays;
                    } else {
                        items = this.bases;
                    }
                    int index = Integer.parseInt(r.group(1));
                    SortedMap<Integer, ImageItem> map = items.get(index);
                    if(map == null) {
                        map = new TreeMap<Integer, ImageItem>();
                        items.put(index, map);
                    }
                    map.put(cursor.getInt(indexSequence), imageItem);
                }
                cursor.moveToNext();
            }
        }
    }

    public int getImageCount() {
        return this.urls.size();
    }

    public void setOnChangeListener(OnChangeListener listener) {
        this.listener = listener;
    }

    public Bitmap getBitmap(int index) {
        AnalyticsTracker tracker = AnalyticsTracker.getInstance(this.activity);
        tracker.trackEvent("getImage", this.category, String.valueOf(index), 1);

        if(index < 0 || index >= this.urls.size()) {
            throw new IllegalArgumentException("Index " + index + " is larger the image list size " + this.urls.size());
        }
        if(!this.urls.containsKey(index)) {
            throw new IllegalStateException("Invalid map");
        }

        Bitmap result = null;
        try {
            Bitmap original = this.loadBitmap(Uri.parse(ContentTables.BitmapTable.CONTENT_URI_STRING + this.category + index), this.urls.get(index), index);
            if(this.overlays.isEmpty() && this.bases.isEmpty()) {
                result = original;
            } else {
                if(original != null) {
                    TextPaint paint = new TextPaint();
                    paint.setColor(Color.BLUE);
                    paint.setTextSize(16);
                    paint.setTypeface(Typeface.DEFAULT_BOLD);

                    result = Bitmap.createBitmap(original.getWidth(), original.getHeight(), Bitmap.Config.ARGB_8888);
                    Canvas canvas = new Canvas(result);

                    if(!this.bases.isEmpty()) {
                        this.drawItems(this.baseCategory, this.bases, canvas, -1, original.getWidth(), original.getHeight(), paint);
                        this.drawItems(this.baseCategory, this.bases, canvas, index, original.getWidth(), original.getHeight(), paint);
                    }

                    canvas.drawBitmap(original, 0, 0, paint);

                    if(!this.overlays.isEmpty()) {
                        this.drawItems(this.overlayCategory, this.overlays, canvas, -1, original.getWidth(), original.getHeight(), paint);
                        this.drawItems(this.overlayCategory, this.overlays, canvas, index, original.getWidth(), original.getHeight(), paint);
                    }
                }
            }
        } catch(IOException e) {
            result = BitmapFactory.decodeResource(this.activity.getResources(), R.drawable.no_picture);
        }

        tracker.free(this.activity);

        if(result == null) {
            return BitmapFactory.decodeResource(this.activity.getResources(), R.drawable.picture_loading);
        } else {
            return result;
        }
    }

    private void drawItems(String category, Map<Integer, SortedMap<Integer, ImageItem>> items, Canvas canvas, int index, int width, int height, Paint paint) {
        SortedMap<Integer, ImageItem> entries = items.get(index);
        if(entries != null) {
            for(Map.Entry<Integer, ImageItem> entry : entries.entrySet()) {
                ImageItem imageItem = entry.getValue();
                if(imageItem.type == ImageItem.TYPE_BITMAP) {
                    try {
                        Bitmap bitmap = this.loadBitmap(Uri.parse(ContentTables.BitmapTable.CONTENT_URI_STRING + category + entry.getKey()), imageItem.data, index);
                        if(bitmap != null) {
                            int posX = imageItem.refWidth == -1 ? imageItem.posX : imageItem.posX * width / imageItem.refWidth;
                            int posY = imageItem.refHeight == -1 ? imageItem.posY : imageItem.posY * height / imageItem.refHeight;
                            canvas.drawBitmap(bitmap, posX, posY, paint);
                        }
                    } catch(IOException e) {
                        // left blank intentionally
                    }
                } else if(imageItem.type == ImageItem.TYPE_TEXT) {
                    canvas.drawText(imageItem.data, imageItem.posX, imageItem.posY, paint);
                }
            }
        }
    }

    private Bitmap loadBitmap(final Uri uri, final String url, final int indexToReport) throws IOException {
        synchronized(this.loadingImages) {
            if(this.loadingImages.contains(url)) {
                return null;
            }

            Bitmap bitmap = null;
            final Cursor cursor = this.activity.getContentResolver().query(uri, null, url, null, null);
            if(cursor != null) {
                if(!cursor.isAfterLast()) {
                    String path = cursor.getString(cursor.getColumnIndex(ContentTables.COLUMN_RESULT));
                    cursor.close();
                    bitmap = BitmapFactory.decodeFile(path);
                    if(bitmap == null) {
                        throw new IOException("Bitmap decoding error");
                    }
                } else {
                    this.loadingImages.add(url);
                    this.activity.startManagingCursor(cursor);
                    cursor.registerContentObserver(new ContentObserver(new Handler()) {
                        @Override
                        public boolean deliverSelfNotifications() {
                            return false;
                        }

                        @Override
                        public void onChange(boolean selfChange) {
                            synchronized(AladinImageListViewAdapter.this.loadingImages) {
                                AladinImageListViewAdapter.this.loadingImages.remove(url);
                                cursor.unregisterContentObserver(this);
                                AladinImageListViewAdapter.this.activity.stopManagingCursor(cursor);
                                cursor.close();
                                if(AladinImageListViewAdapter.this.listener != null) {
                                    AladinImageListViewAdapter.this.listener.onChange(indexToReport);
                                }
                            }
                        }
                    });
                }
            } else {
                throw new IOException("Bitmap loading error");
            }
            return bitmap;
        }
    }
}
